#include <iostream>
#include <wait.h>
#include <cstring>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string>
#include <signal.h>
#include "task.hpp"
#include "thread_pool.hpp"

//版本1:单进程版本
// void usage(std::string proc)
// {
//   std::cout << "usage:\n\t" << proc << ": need port" << std::endl;
// }

// void service_IO(int new_sock)
// {
//   //提供服务,我们是一个死循环
//   while(true)
//   {
//     char buffer[1024];
//     bzero(buffer, strlen(buffer));

//     ssize_t ret = read(new_sock, buffer, sizeof(buffer) - 1);
//     if (ret > 0)
//     {
//       //这里我们默认获取的内容是字符串
//       buffer[ret] = '\0';
//       std::cout << "client# " << buffer << std::endl;
      
//       std::string echo = "server# ";
//       echo += buffer;

//       write(new_sock, echo.c_str(), echo.size());
//     }
//     else if (ret == 0)
//     {
//       std::cout << "client quit !" << std::endl;
//       break;
//     }
//     else 
//     {
//       perror("read");
//       std::cerr << "errno : " << errno << std::endl;
//       break;
//     }
//   }
// }

// int main(int argc, char* argv[])
// {

//   if (argc != 2)
//   {
//     usage(argv[0]);
//     return 1;
//   }
  
//   //1.创建监听套接字
  
//   int listen_sock = socket(AF_INET, SOCK_STREAM, 0);
//   if (listen_sock < 0)
//   {
//     perror("create listen_sock");
//     std::cout << "errno : " << errno << std::endl;
//   }

//   //2.进行网络地址信息的绑定
//   struct sockaddr_in local; 
//   bzero(&local, sizeof(local));
//   local.sin_family = AF_INET;
//   local.sin_port = htons(atoi(argv[1]));
//   local.sin_addr.s_addr = INADDR_ANY;
  
//   if (bind(listen_sock, (struct sockaddr*)&local, sizeof(local)) < 0)
//   {
//     perror("bind");
//     std::cout << "errno : " << errno << std::endl;
//     return 3;
//   }

//   //3.因为tcp是面向连接的,通信之间需要建立连接,然后才能进行通信
//   //一定有一方主动建立建立连接(client),一定有一方被动接收连接(server)
//   //我们现在是server,所以我们需要不间断的等待client的到来
//   //我们需要给用户提供一个建立连接的功能
//   //设置套接字位listen状态,本质是允许用户连接
//   const int back_log = 5;

//   //listen()声明sockfd处于监听状态
//   //并且最多允许由back_log个客户端处于连接等待状态
//   //如果接收到更多的连接请求就忽略,这里一般不会设置太大(一般是5)
//   //listen()成功返回0,失败返回-1
//   if (listen(listen_sock, back_log) < 0)
//   {
//     perror("listen");
//     std::cout << "errno : " << errno << std::endl;
//     return 4;
//   }

//   while(true)
//   {
//     struct sockaddr_in peer;
//     bzero(&peer, sizeof(peer));
//     socklen_t len = sizeof(peer);

//     int new_sock = accept(listen_sock, (struct sockaddr*)&peer, &len);
//     if (new_sock < 0)
//     {
//       continue;
//     }

//     std::cout << "get a new link !" << std::endl;

//     //到这里就说明接收成功了,可以提供服务了
//     service_IO(new_sock);
//   }
//   return 0;
// }

//连接多个客户端的情况
//再启动一个客户端,尝试连接服务器,发现第二个客户端,不能正确和服务器进行通信
//那是因为accept了一个请求之后,就在一直while循环尝试read
//没有继续调用accept,导致不能接收新的请求
//版本2:多进程版
// void usage(std::string proc)
// {
//   std::cout << "usage:\n\t" << proc << ": need port" << std::endl;
// }

// void service_IO(int new_sock)
// {
//   //提供服务,我们是一个死循环
//   while(true)
//   {
//     char buffer[1024];
//     bzero(buffer, strlen(buffer));

//     ssize_t ret = read(new_sock, buffer, sizeof(buffer) - 1);
//     if (ret > 0)
//     {
//       //这里我们默认获取的内容是字符串
//       buffer[ret] = '\0';
//       std::cout << "client# " << buffer << std::endl;
      
//       std::string echo = "server# ";
//       echo += buffer;

//       write(new_sock, echo.c_str(), echo.size());
//     }
//     else if (ret == 0)
//     {
//       std::cout << "client quit !" << std::endl;
//       break;
//     }
//     else 
//     {
//       perror("read");
//       std::cerr << "errno : " << errno << std::endl;
//       break;
//     }
//   }
// }

// int main(int argc, char* argv[])
// {

//   if (argc != 2)
//   {
//     usage(argv[0]);
//     return 1;
//   }
  
//   //1.创建监听套接字
  
//   int listen_sock = socket(AF_INET, SOCK_STREAM, 0);
//   if (listen_sock < 0)
//   {
//     perror("create listen_sock");
//     std::cout << "errno : " << errno << std::endl;
//   }

//   //2.进行网络地址信息的绑定
//   struct sockaddr_in local; 
//   bzero(&local, sizeof(local));
//   local.sin_family = AF_INET;
//   local.sin_port = htons(atoi(argv[1]));
//   local.sin_addr.s_addr = INADDR_ANY;
  
//   if (bind(listen_sock, (struct sockaddr*)&local, sizeof(local)) < 0)
//   {
//     perror("bind");
//     std::cout << "errno : " << errno << std::endl;
//     return 3;
//   }

//   //3.因为tcp是面向连接的,通信之间需要建立连接,然后才能进行通信
//   //一定有一方主动建立建立连接(client),一定有一方被动接收连接(server)
//   //我们现在是server,所以我们需要不间断的等待client的到来
//   //我们需要给用户提供一个建立连接的功能
//   //设置套接字位listen状态,本质是允许用户连接
//   const int back_log = 5;

//   //listen()声明sockfd处于监听状态
//   //并且最多允许由back_log个客户端处于连接等待状态
//   //如果接收到更多的连接请求就忽略,这里一般不会设置太大(一般是5)
//   //listen()成功返回0,失败返回-1
//   if (listen(listen_sock, back_log) < 0)
//   {
//     perror("listen");
//     std::cout << "errno : " << errno << std::endl;
//     return 4;
//   }

//   //子进程退出的时候,会给父进程发送SISCHLD的信号
//   //我们设置SIGCHLD信号的处理方式是SIG_IGN(默认的处理方式是忽略)
//   //在linux中父进程忽略子进程的SIGCHLD信号,子进程会自动退出释放资源
//   //signal(SIGCHLD, SIG_IGN);

//   while(true)
//   {
//     struct sockaddr_in peer;
//     bzero(&peer, sizeof(peer));
//     socklen_t len = sizeof(peer);

//     int new_sock = accept(listen_sock, (struct sockaddr*)&peer, &len);
//     if (new_sock < 0)
//     {
//       continue;
//     }

//     uint16_t client_port = ntohs(peer.sin_port);
//     //这个函数的作用有两点
//     //1.将4字节网络序列数据转换成主机序列
//     //2.将4字节IP转换成点分十进制的字符串风格的IP
//     std::string client_ip = inet_ntoa(peer.sin_addr);

//     std::cout << "get a new link : ["  << client_ip << ":" << client_port << "]# " << new_sock << std::endl;

//     //到这里就说明接收成功了,可以提供服务了
//     pid_t id = fork();
//     if (id < 0)
//     {
//       //create fail
//       continue;
//     }

//     //写法1:如果要使用,记得把上面的signal函数去掉注释
//     //else if (id == 0)
//     //{
//     //  //child
//     //  //因为监听是父进程的任务,所以子进程不用,就把监听套接字进行关闭
//     //  close(listen_sock);

//     //  service_IO(new_sock);
//     //  close(new_sock);
//     //  exit(0);
//     //}
//     //else 
//     //{
//     //  //father
//     //  //因为new_sock是子进程所使用的套接字,父进程不进行使用,所以可以进行关闭
//     //  close(new_sock);
//     //}
    
//     //写法2:不使用signal函数
//     else if (id == 0)
//     {
//       //child
//       //因为监听是父进程的任务,所以子进程不用,就把监听套接字进行关闭
//       close(listen_sock);

//       if (fork() > 0)
//       {
//         //子进程退出
//         exit(0);
//       }
      
//       //孙进程向后走
//       //这个时候孙进程是孤儿进程,交给系统进行处理
//       service_IO(new_sock);
//       close(new_sock);
//       exit(0);
//     }
//     else 
//     {
//       //father
      
//       //因为上面的子进程退出的很快,所以这里几乎不会卡顿
//       waitpid(id, nullptr, 0);

//       //因为new_sock是子进程所使用的套接字,父进程不进行使用,所以可以进行关闭
//       close(new_sock);
//     }
//   }
//   return 0;
// }

//版本3:多线程版
// void usage(std::string proc)
// {
//   std::cout << "usage:\n\t" << proc << ": need port" << std::endl;
// }

// void service_IO(int new_sock)
// {
//   //提供服务,我们是一个死循环
//   while(true)
//   {
//     char buffer[1024];
//     bzero(buffer, strlen(buffer));

//     ssize_t ret = read(new_sock, buffer, sizeof(buffer) - 1);
//     if (ret > 0)
//     {
//       //这里我们默认获取的内容是字符串
//       buffer[ret] = '\0';
//       std::cout << "client# " << buffer << std::endl;
      
//       std::string echo = "server# ";
//       echo += buffer;

//       write(new_sock, echo.c_str(), echo.size());
//     }
//     else if (ret == 0)
//     {
//       std::cout << "client quit !" << std::endl;
//       break;
//     }
//     else 
//     {
//       perror("read");
//       std::cerr << "errno : " << errno << std::endl;
//       break;
//     }
//   }
// }

// void* hanlder_request(void* args)
// {
//   //首先进来先把自己这个线程进行分离,避免主线程阻塞等待回收其他线程的资源
//   pthread_detach(pthread_self());
//   int sock = *(int*)args;
//   delete (int*)args;

//   service_IO(sock);
//   close(sock);
//   return nullptr;
// }

// int main(int argc, char* argv[])
// {

//   if (argc != 2)
//   {
//     usage(argv[0]);
//     return 1;
//   }
  
//   //1.创建监听套接字
  
//   int listen_sock = socket(AF_INET, SOCK_STREAM, 0);
//   if (listen_sock < 0)
//   {
//     perror("create listen_sock");
//     std::cout << "errno : " << errno << std::endl;
//   }

//   //2.进行网络地址信息的绑定
//   struct sockaddr_in local; 
//   bzero(&local, sizeof(local));
//   local.sin_family = AF_INET;
//   local.sin_port = htons(atoi(argv[1]));
//   local.sin_addr.s_addr = INADDR_ANY;
  
//   if (bind(listen_sock, (struct sockaddr*)&local, sizeof(local)) < 0)
//   {
//     perror("bind");
//     std::cout << "errno : " << errno << std::endl;
//     return 3;
//   }

//   //3.因为tcp是面向连接的,通信之间需要建立连接,然后才能进行通信
//   //一定有一方主动建立建立连接(client),一定有一方被动接收连接(server)
//   //我们现在是server,所以我们需要不间断的等待client的到来
//   //我们需要给用户提供一个建立连接的功能
//   //设置套接字位listen状态,本质是允许用户连接
//   const int back_log = 5;

//   //listen()声明sockfd处于监听状态
//   //并且最多允许由back_log个客户端处于连接等待状态
//   //如果接收到更多的连接请求就忽略,这里一般不会设置太大(一般是5)
//   //listen()成功返回0,失败返回-1
//   if (listen(listen_sock, back_log) < 0)
//   {
//     perror("listen");
//     std::cout << "errno : " << errno << std::endl;
//     return 4;
//   }

//   //子进程退出的时候,会给父进程发送SISCHLD的信号
//   //我们设置SIGCHLD信号的处理方式是SIG_IGN(默认的处理方式是忽略)
//   //在linux中父进程忽略子进程的SIGCHLD信号,子进程会自动退出释放资源
//   //signal(SIGCHLD, SIG_IGN);

//   while(true)
//   {
//     struct sockaddr_in peer;
//     bzero(&peer, sizeof(peer));
//     socklen_t len = sizeof(peer);

//     int new_sock = accept(listen_sock, (struct sockaddr*)&peer, &len);
//     if (new_sock < 0)
//     {
//       continue;
//     }

//     uint16_t client_port = ntohs(peer.sin_port);
//     //这个函数的作用有两点
//     //1.将4字节网络序列数据转换成主机序列
//     //2.将4字节IP转换成点分十进制的字符串风格的IP
//     std::string client_ip = inet_ntoa(peer.sin_addr);

//     std::cout << "get a new link : ["  << client_ip << ":" << client_port << "]# " << new_sock << std::endl;

//     //到这里就说明接收成功了,可以提供服务了
//     pthread_t tid;
//     int* sock = new int(new_sock);
//     pthread_create(&tid, nullptr, hanlder_request, (void*)sock);

//   }
//   return 0;
// }

//版本4:线程池版
void usage(std::string proc)
{
  std::cout << "usage:\n\t" << proc << ": need port" << std::endl;
}

void service_IO(int new_sock)
{
  //提供服务,我们是一个死循环
  while(true)
  {
    char buffer[1024];
    bzero(buffer, strlen(buffer));

    ssize_t ret = read(new_sock, buffer, sizeof(buffer) - 1);
    if (ret > 0)
    {
      //这里我们默认获取的内容是字符串
      buffer[ret] = '\0';
      std::cout << "client# " << buffer << std::endl;
      
      std::string echo = "server# ";
      echo += buffer;

      write(new_sock, echo.c_str(), echo.size());
    }
    else if (ret == 0)
    {
      std::cout << "client quit !" << std::endl;
      break;
    }
    else 
    {
      perror("read");
      std::cerr << "errno : " << errno << std::endl;
      break;
    }
  }
}

void* hanlder_request(void* args)
{
  //首先进来先把自己这个线程进行分离,避免主线程阻塞等待回收其他线程的资源
  pthread_detach(pthread_self());
  int sock = *(int*)args;
  delete (int*)args;

  service_IO(sock);
  close(sock);
  return nullptr;
}

int main(int argc, char* argv[])
{

  if (argc != 2)
  {
    usage(argv[0]);
    return 1;
  }
  
  //1.创建监听套接字
  
  int listen_sock = socket(AF_INET, SOCK_STREAM, 0);
  if (listen_sock < 0)
  {
    perror("create listen_sock");
    std::cout << "errno : " << errno << std::endl;
  }

  //2.进行网络地址信息的绑定
  struct sockaddr_in local; 
  bzero(&local, sizeof(local));
  local.sin_family = AF_INET;
  local.sin_port = htons(atoi(argv[1]));
  local.sin_addr.s_addr = INADDR_ANY;
  
  if (bind(listen_sock, (struct sockaddr*)&local, sizeof(local)) < 0)
  {
    perror("bind");
    std::cout << "errno : " << errno << std::endl;
    return 3;
  }

  //3.因为tcp是面向连接的,通信之间需要建立连接,然后才能进行通信
  //一定有一方主动建立建立连接(client),一定有一方被动接收连接(server)
  //我们现在是server,所以我们需要不间断的等待client的到来
  //我们需要给用户提供一个建立连接的功能
  //设置套接字位listen状态,本质是允许用户连接
  const int back_log = 5;

  //listen()声明sockfd处于监听状态
  //并且最多允许由back_log个客户端处于连接等待状态
  //如果接收到更多的连接请求就忽略,这里一般不会设置太大(一般是5)
  //listen()成功返回0,失败返回-1
  if (listen(listen_sock, back_log) < 0)
  {
    perror("listen");
    std::cout << "errno : " << errno << std::endl;
    return 4;
  }

  //子进程退出的时候,会给父进程发送SISCHLD的信号
  //我们设置SIGCHLD信号的处理方式是SIG_IGN(默认的处理方式是忽略)
  //在linux中父进程忽略子进程的SIGCHLD信号,子进程会自动退出释放资源
  //signal(SIGCHLD, SIG_IGN);

  while(true)
  {
    struct sockaddr_in peer;
    bzero(&peer, sizeof(peer));
    socklen_t len = sizeof(peer);

    int new_sock = accept(listen_sock, (struct sockaddr*)&peer, &len);
    if (new_sock < 0)
    {
      continue;
    }

    uint16_t client_port = ntohs(peer.sin_port);
    //这个函数的作用有两点
    //1.将4字节网络序列数据转换成主机序列
    //2.将4字节IP转换成点分十进制的字符串风格的IP
    std::string client_ip = inet_ntoa(peer.sin_addr);

    std::cout << "get a new link : ["  << client_ip << ":" << client_port << "]# " << new_sock << std::endl;

    //到这里就说明接收成功了,可以提供服务了
    //我们之前的多进程和多线程的版本都有一些缺点
    //1.创建线程和进程都是无上限的,这样很容易导致系统崩溃
    //2.当客户的连接来了,我们才给客户创建进程和线程
    //这样客户的时间成本就会增加
    qds::task t(new_sock);
    qds::thread_pool<qds::task>::get_instance()->push_task(t);

  }
  return 0;
}
